package com.study.factory;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Qi XueSong
 */
public class BeanFactory {

    //将生产的对象放入到map中 id:对象id  value：生产的对象
    private static Map<String,Object> map = new HashMap<>();

    /**
     * 此静态块，在此类实例化的时候执行
     */
    static {
        //将配置bean 的文件读取成字节流
        InputStream resource = BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.xml");
        try {
            //通过dom4j 对xml 进行读取
            Document document = new SAXReader().read(resource);

            //通过xpath技术 读取xml中所有的bean标签
            List<Element> list = document.selectNodes("//bean");
            for (Element element : list) {
                //id 即为 map 中的key
                String id = element.attributeValue("id");
                //获取到配置类的全限定类名
                String clazz = element.attributeValue("class");
                //根据全限定类名获取到类型
                Class<?> aClass = Class.forName(clazz);
                //通过反射技术实例化对象
                Object o = aClass.newInstance();
                // 将所有实例化的对象放入到map中
                map.put(id,o);
            }

            // 获取到所以 property 标签
            List<Element> propertyList = document.selectNodes("//property");
            //遍历
            for (Element property : propertyList) {
                //获取当前标签的父标签
                Element parent = property.getParent();
                //获取到该父标签中的标识
                String parentId = parent.attributeValue("id");
                //根据标识在map中查找对应的类
                Object parentObj = map.get(parentId);

                // 获取property标签中的属性值
                String name = property.attributeValue("name");
                String ref = property.attributeValue("ref");

                //获取父标签对应类中的所有方法
                Method[] methods = parentObj.getClass().getMethods();
                //遍历所有方法
                for (Method method : methods) {
                    //如果方法名称和 set + name 的值相等（setTransferService），则为set中的对象赋值
                    if(method.getName().equalsIgnoreCase("set"+name)){
                        //调用某个对象的set方法
                        method.invoke(parentObj,map.get(ref));
                    }
                }
                //将给set方法赋值后的对象放回到map中
                map.put(parentId,parentObj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 对外提供获取实例对象的接口（根据id获取）
     */
    public static  <T> T getBean(String id){
        return (T)map.get(id);
    }
}
